package com.dj.product.k2faas.common.utils;

import cn.hutool.core.util.ReflectUtil;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.util.Assert;

import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.List;

/**
 * Created by 丁建 on 2025/9/23 08:41
 *
 * @author 丁建
 */
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class BeanUtil {

    private static <T> Constructor<T> getResolvableConstructor(Class<T> clazz) {
        Constructor<?>[] constructors = clazz.getDeclaredConstructors();
        //找参数最多的那一个
        Constructor<?> constructor = null;
        var currentArgsCount = 0;
        for (var c : constructors) {
            int mod = c.getModifiers();
            if (!Modifier.isPublic(mod)) {
                continue;
            }

            if (c.getParameters().length >= currentArgsCount) {
                constructor = c;
                currentArgsCount = constructor.getParameters().length;
            }
        }

        Assert.isTrue(constructor != null, "未找到类：" + clazz + "的可用构造器。构造器在使用时，必须为Public修饰。");

        return (Constructor<T>) constructor;
    }

    /**
     * 对象转换,单个对象的转换
     * 工作逻辑，从tClass的构造器去选取一个参数最少的构造器。并进行构造。
     * 构造器的参数值从source中的成员变量去取
     * 不管在哪个包，只支持Public的构造器
     *
     * @param <T>    目标对象类型
     * @param source 源Bean对象
     * @param tClass 目标Class
     * @return 目标对象
     */
    public static <T> T convert(Object source, Class<T> tClass) {
        var constructor = getResolvableConstructor(tClass);

        //参数列表
        var parameters = constructor.getParameters();
        var argValues = new Object[parameters.length];
        var sourceClass = source.getClass();

        for (var i = 0; i < parameters.length; i++) {
            var parameterName = parameters[i].getName();

            //确保构造器的参数在source中能正确取到值
            var field = ReflectUtil.getField(sourceClass, parameterName);
            Assert.isTrue(field != null, "类：" + sourceClass + "未定义属性：" + parameterName + "但构造器中需要使用该参数。");

            var value = ReflectUtil.getFieldValue(source, parameterName);
            argValues[i] = value;
        }

        var newBean = BeanUtils.instantiateClass(constructor, argValues);
        BeanUtils.copyProperties(source, newBean);

        return newBean;

    }

    /**
     * 分页信息转换
     *
     * @param page        从数据库查询到的数据列表
     * @param resultClass 转换后的Page携带的类型
     * @param pageable    前端传递的分页参数
     * @param <R>         期望返回的类型
     * @return 包装新类型的分页数据
     */
    public static <R> Page<R> toPage(Page<?> page, Class<R> resultClass, Pageable pageable) {
        var list = page.getContent();
        var resultList = toList(list, resultClass);

        return new PageImpl<>(resultList, pageable, page.getTotalElements());
    }

    /**
     * 列表信息转换
     *
     * @param list        数据列表
     * @param resultClass 转换后的Page携带的类型
     * @param <R>         期望返回的类型
     * @return 包装新类型的列表
     */
    public static <R> List<R> toList(List<?> list, Class<R> resultClass) {
        return list.stream().map(c -> convert(c, resultClass)).toList();
    }

    /**
     * 创建一个空对像
     */
    public static <T> T construct(Class<T> tClass) {
        var constructor = getResolvableConstructor(tClass);

        //参数列表
        var parameters = constructor.getParameters();
        Assert.isTrue(parameters.length == 0, "不支持有参构造器。");

        return BeanUtils.instantiateClass(constructor);
    }

    public static void copyProperties(Object source, Object target) {
        BeanUtils.copyProperties(source, target);
    }
}
